
/*
    Simple Easier Game.
    Start 231209-17:36:00
*/

#define UNICODE
#define _UNICODE
#pragma once
#pragma comment(lib, "F:\\MSVC\\installer\\VC\\Auxiliary\\VS\\lib\\x64\\EasyXw.lib")
#pragma comment(lib, "E:\\Environments\\cl\\seg\\superimage.lib")
#pragma comment(lib, "User32.lib")
#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "Kernel32.lib")
#pragma comment(lib, "shlwapi.lib")
#include <graphics.h>
#include <Shlwapi.h>
#include <chrono>
#include <exception>
#include <random>
#include "superimage.h"

// 字符串最大长度
constexpr int Seg_StringLengthMax = 269;
// 列表、字典最大长度
constexpr int Seg_ListLengthMax   = 8;
typedef   unsigned char             color_t;

namespace seg
{
    HWND MainHandle;

    // dont use template & heapAlloc!
    /* String */
    class String
    {
        private:
            wchar_t s[Seg_StringLengthMax];
            int _len;
        public:
            int len();
            // 字符串追加内容
            int join(const wchar_t *const buf);
            bool join(const wchar_t c);
            void join (int d);
            void join(double f);
            void join(bool b);
            // 字符串输入流
            seg::String& operator>>(const wchar_t *const);
            seg::String& operator>>(int d);
            seg::String& operator>>(double f);
            seg::String& operator>>(bool b);
            // 字符串初始化
            seg::String& operator=(const wchar_t *const);
            void back(); // backspace
            // 字符串初始化
            void set(const wchar_t *const buf);
            const wchar_t *get();
            /*计算字符串中有多少行, 行分隔符指定为sep*/
            int count_line(wchar_t sep = (wchar_t)('\n'));
            /*获得字符串中第line行的内容, 行分隔符为sep. 若line大于实际行数, 返回false; 否则返回true*/
            bool get_line(wchar_t * out, int line = 0, wchar_t sep = (wchar_t)('\n'));
            // 判断字符串是否为正浮点数
            bool isfloat();
            bool isfloat(int start);
            // 判断字符串是否为正整数
            bool isint();
            bool isint(int start);
            bool isbool(); // True/False
            bool IsCppBool(); // true/false
            /* 类型转换 */
            int cint();
            double cfloat();
            bool cbool();
            bool cbool(bool _raise); // str不是bool值是否抛出异常
            // 去除字符串左侧由(c)指定的字符
            seg::String lstrip(wchar_t * c);
            // return a new string
            seg::String rstrip(wchar_t * c);
            // not return new one, WHETHER arg[1] is or not TRUE !
            void rstrip(wchar_t * c, bool get_a_new_string = false);
            seg::String strip(wchar_t * c);
            bool startswith(wchar_t *s);
            bool endswith(wchar_t *s);
            seg::String reverse();
            bool equals(const wchar_t *s);
            bool operator ==(const wchar_t *s);
            bool operator ==(seg::String &s);
            // 获得字符串 range: [start, stop)
            // stop为负数时, 为len + stop + 1.
            seg::String get(int start, int stop = -1, int step = 1);
            // index < 0 , index = len + index.
            wchar_t index(int _index);
            wchar_t operator[](int _index);
            wchar_t operator[](int _index) const;
            /* range: [index, _len)*/
            int find(wchar_t c, int index = 0);
            int count(wchar_t c);
            int FindFrom(const wchar_t * const slist );   // 从字符串中能找到的列表里包含有的字符的个数
            void from_cchar(const char * cs); // experimental, may get error res.
            void to_cchar(char * out);        // exp, likelihood err.
            String();
    };

    /* Any type variable */
    enum SegType
    {
        sNone, sint, sfloat, sstr, sdict, sbool
    };

    // Basic data
    class SegObject_BasicObject
    {
    private:
        union
        {
            bool b;
            int d;
            double f;
        }var;
        seg::String s;
        seg::SegType _t;
    public:
        //SegObject_BasicObject(seg::SegType t, void *value);
        //SegObject_BasicObject(seg::SegType t, int d = 0, double f = 0.0, wchar_t *s = NULL);
        SegObject_BasicObject();
        seg::SegType ctype();
        // Not recommended to use this method.
        //void* get();
        int intvar();
        double floatvar();
        seg::String &strvar();
        void set(int);
        void set(double);
        void set(const wchar_t *);
        void set(seg::String &);
        void set(bool);
        void set(seg::SegObject_BasicObject &b);
    };

    // dict, no order
    class SegObject_DictObject
    {
    private:
        int _len;
        bool isused[Seg_ListLengthMax];
        seg::String _key[Seg_ListLengthMax];
        seg::SegObject_BasicObject _value[Seg_ListLengthMax];
    public:
        SegObject_DictObject();
        int len();
        bool set(seg::String &__key, seg::SegObject_BasicObject &__value);
        bool set(const wchar_t *__key_, seg::SegType t, int d = 0, double f = 0.0, bool b = false, const wchar_t * s = NULL);
        // 从形如 xx : xxx, xx : xxxx, ... 的字符串中读取数据, 这不会刷新整个字典, 也不会删除之前存放的数据
        int FromStr(seg::String &);
        // 以字符串 {"xx" : xxx, "xx" : "qwerty", ...} 的形式返回字典所有元素
//        seg::String __str__();
        // 根据key获得value地址
        seg::SegObject_BasicObject &get(const wchar_t *key);
        // 根据index获得字典的键, 若index不正确将引发异常
//        seg::String &getkey(int index = 0);
        // 获得index之后第一个可用的键. 若index之后没有可用的键 or index过大, 返回字典首项的地址
//        seg::String &get_nextkey(int from_index = 0);
        // if can't, return -1
//        int get_nextvalidkey(int from_index = 0);
//        bool del(const wchar_t *key);
//        bool del(int index);
        // 清除所有键值对
//        int clean();
    };

    // 数据读取, 交换
    /*
    class SegObject
    {
    private:
        seg::SegObject_BasicObject var;
        seg::SegObject_DictObject _dict;
        seg::SegType _t;
    public:
        SegObject();
        SegObject(seg::SegType t, int d = 0, double f = 0.0, bool b = false, wchar_t *s = NULL);
        ~SegObject();
        seg::SegType ctype();
        void* get();
        seg::SegObject_BasicObject &getbasic();
        seg::SegObject_DictObject  &getdict();
        void set(int);
        void set(double);
        void set(wchar_t *);
        void set(seg::String &);
        void set(seg::SegObject_BasicObject &);
        void set(seg::SegObject_DictObject &);
        void set_dict(seg::String &);
        void set(bool);
    };
    */

    /* Key Message */
    namespace Key
    {
        int GetRecentRealKey();// A-Z
        int GetVirtualKey(int);// VK_xxx
        inline bool GetGlobalKeyPressed(int VK); // KeyCode
    } // namespace Key


    /*可点击的控件, 使用函数指针访问点击(OnClick)和渲染(render)功能*/
    class ClickableObject
    {
    public:
        int x;
        int y;
        int width;
        int height;
        int LineWidth;
        int ComType; // 控件类型, 自定义
        int id;
        COLORREF fg; COLORREF bg; seg::String s; // 常用属性
        // return value: 0:None, 1:Quit mainloop
        int (*OnClick)(seg::ClickableObject &);
        void (*render)(seg::ClickableObject &);
        int (*OnClickEx)(seg::ClickableObject &, MOUSEMSG & , double, double);
        bool AutoRender;
        bool ClickAsInput;
        bool UsingOnclickEx;
        COLORREF TextColor;
        int LineStyle;
        /*x0 y0: 控件左上角坐标
        w0 h0: 控件宽、高
        lw ct: 线宽, 控件类型(自定义)
        fg0 bg0: 边框、背景颜色
        ar: Autorender. if true, render it as a label(with string 's'.)
        id: 该控件的ID
        cai: click_as_input (Text only)
        uoce: 使用高级的onclick函数, 接收鼠标消息, 同时获得鼠标相对于控件的坐标(以百分比表示, as double)
        tc: Text color.;  ls: linestyle
        wchar_t s[] *OnClick(self) *render(self): 自定义*/
        void init
        (   int x0, int y0, int w0, int h0, int lw, int ct,
            COLORREF fg0, COLORREF bg0, bool ar, int Id, bool cai,
            bool uoce, COLORREF tc = RGB(0,0,0), int ls = PS_SOLID);
        int _on_click();
        int _on_click(MOUSEMSG & _m, double _perx, double _pery);
        void _render();
    };

    /*简单的界面*/
    class Interface
    {
        public:
            seg::ClickableObject * cbs;
        private:
            int num; // num of cbs
            /*检查鼠标点击动作是否触发回调函数
            回调函数返回bool值。如果为true, 则退出mainloop*/
            bool CheckChoose(MOUSEMSG Mmsg);
            // 选中的控件, 可向其发送消息
            int ChooseID;// [0, num); if None, this = -1.
        public:
            COLORREF bg;
            bool ShowDebugInfo;
        private:
            bool CreateAutoQuitButton;
            seg::String dbgifo;
            int dbgifo_livetime;
        public:
            Interface(int num, COLORREF BG, bool AutoExitBtn);
            ~Interface();
            void mainloop(); // 循环播放
    };

    void NoneTypeAllFuncPtr(seg::ClickableObject &p);
    int NoneTypeOfFuncPtr(seg::ClickableObject & p);
    int NoneTypeOfFuncPtrEx(seg::ClickableObject & p, MOUSEMSG & m, double perx, double pery);
    void ELLIPSIS(){return;}
    void EasyX_Initialize(int width, int height, LPCWSTR WinTitle);
    void EasyX_Initialize(int width, int height, LPCWSTR WinTitle, int flag);

    //
    namespace Times
    {
        class Clock{
            private:
                time_t T0;
                int Tick;
                unsigned int frame; // frame in one second, to calc fps
                unsigned int fps;
                unsigned int ticks_in_one_second;
                unsigned int tps;
                unsigned int fps_min;
                unsigned int fps_min_ask;
                long long fps_min_tick;
            public:
                unsigned int get_fps();
                void tick(int t);
                void tick();
                void init();
                void set_tps(){
                    this->ticks_in_one_second += 1;
                };
                unsigned int ask_tps(){
                    return this->tps;
                };
                unsigned int ask_fpsmin() {
                    return this->fps_min_ask;
                }
        };
        long long get_ticks();
        auto get_ticks_ns();
    } // Times
    namespace render
    {
        enum Place {
            lefttop, righttop, leftbottom, rightbottom,
            left, right, bottom, top, center
        };
        void text(int, int, TCHAR*, seg::render::Place CharacterPos);
        void multiline(int, int, seg::String, int, seg::render::Place CharacterPos);
        void fill(COLORREF colorf = RGB(30, 30, 60))
        {
            setbkcolor(colorf);
            cleardevice();
        }
        void flip() {
            FlushBatchDraw();
        }
        void update() {
            FlushBatchDraw();
        }
        void update(int x, int y, int right, int btm) {
            FlushBatchDraw(x, y, right, btm);
        }
    }
    // 图像处理
    namespace Pillow
    {
        enum Directs
        {
            X = 0b0001, Y = 0b0010
        };
        typedef struct
        {
            color_t r;
            color_t g;
            color_t b;
        }ColorTuple;
        const ColorTuple WhiteColor{255, 255, 255};
        /*图像翻转
        Direct: Directs枚举类中*/
        void Flip(int direct, IMAGE  *source, IMAGE * out);
        // 透明贴图函数
        // 参数：
        //		dstimg: 目标 IMAGE 对象指针。NULL 表示默认窗体
        //		x, y:	目标贴图位置
        //		srcimg: 源 IMAGE 对象指针。NULL 表示默认窗体
        //
        void transparentimage(IMAGE *dstimg, int x, int y, IMAGE *srcimg, UINT transparentcolor);
        /* ImageEx */
        class PIC
        {
            /* Pillow image class */
            public:
                IMAGE img;
                DWORD * IMGBUF;
                int colorkey;
                seg::Pillow::ColorTuple AlphaColor{255, 255, 255};
                PIC(IMAGE * self = NULL, int ColorKey = -1);
                void ToBuffer();
                void blit(int x, int y);
                void blit(int x, int y, int alpha);
                void blit(int x, int y, COLORREF Replace, COLORREF ReplaceTo); // 将Replace替换成ReplaceTo, 忽略其他
                //void blit(int x, int y, int xStart, int yStart, int width, int height, IMAGE *to);
                // func 'blit' From https://blog.csdn.net/qq_43655831/article/details/106203520
                /* 从文件加载图片 */
                bool load(wchar_t *filename, bool _raise = false);
                /*P： 保存缩放后的图像。
                ▪Q： 要缩放的原图像。
                ▪ZoomRate： 缩放比例,不填写ZoomRate2则默认图像横向拉伸和纵向拉伸比率均为ZoomRate。
                ▪HighQuality： 布尔变量，默认为false，填true可选是否使用高质量缩放(双线性插值),默认情况下为常规缩放。
                ZoomRate2：第二缩放比例。不填写则默认图像横纵同比例缩放，填写则前面的ZoomRate变为图像横向拉伸比例，ZoomRate2则变为图像纵向拉伸比例。*/
                void scale(IMAGE * P, IMAGE *Q, double ZoomRate,bool HighQuality=false,double ZoomRate2=0);
                void scale(double ZoomRate,bool HighQuality=false,double ZoomRate2=0);
                /*dstimg: 指定目标 IMAGE 对象指针，用来保存旋转后的图像。
                srcimg: 指定原 IMAGE 对象指针。
                radian: 指定旋转的角度。
                bkcolor: 指定旋转后产生的空白区域的颜色。默认为white色。
                autosize: 指定目标 IMAGE 对象是否自动调整尺寸以完全容纳旋转后的图像。默认为 false。
                highquality: 指定是否采用高质量的旋转。在追求性能的场合请使用低质量旋转。默认为 true。*/
                void rotate(IMAGE *dstimg,IMAGE *srcimg,double radian,COLORREF bkcolor = WHITE,bool autosize = false,bool highquality = true);
                void rotate(double radius,COLORREF bkcolor = WHITE,bool autosize = false,bool highquality = true);
        };
    } // namespace Pillow

    // My Exception
    namespace Traceback
    {
        class Exception
        {
            public:
                Exception(
                    wchar_t * ErrName,
                    wchar_t *FuncName,
                    wchar_t* ErrDesc
                );
                seg::String errName;
                seg::String funcName;
                seg::String errDesc;
                seg::String format_exc;
        };
        seg::String err;
        void raise(wchar_t *en, wchar_t *fn, wchar_t *ed);
        void raise(const wchar_t *en, const wchar_t *fn);
        int errorscreen(seg::Traceback::Exception & se);
        int errorscreen(const std::exception &e);
    } // namespace Traceback

    // Safe classlib
    namespace CSafe
    {
        template <typename t> t mid(t value, t _min, t _max)
        {
            if (value < _min) value = _min;
            else if (value > _max) value = _max;
            return value;
        }
    } // namespace CSafe

    // Window msg
    class WindowC
    {
        private:
            int width;
            int height;
            IMAGE *SCR; // 绘图设备
        public:
            int GetWidth(){return this->width;};
            int GetHeight(){return this->height;};
            friend void seg:: EasyX_Initialize(int width, int height, LPCWSTR WinTitle);
            friend void seg::EasyX_Initialize(int width, int height, LPCWSTR WinTitle, int flag);
    };
    seg::WindowC Window;

    // mathlib
    namespace Math
    {
        std::random_device rd;
        std::default_random_engine eng(rd());
        int randint(int _min, int _max);
        double random(double _min, double _max);
        template <typename T> T iabs(T v)
        {
            return v >= 0 ? v : (-v);
        }
        /*从value生成区间在[value-arg, value+arg]的随机数*/
        template <typename T> T random_spread(T value, T arg)
        {
            T sadd = seg::Math::random(0, ((double)(seg::Math::iabs(arg))) * 2);
            sadd -= arg;
            return (T)(value + sadd);
        }
        // 检查矩形是否相交
        template <typename T> bool checkRectCrash(
            T x1, T y1, T x2, T y2, 
            T x3, T y3, T x4, T y4
        )
        {
            if ((max(x1, x3) <= min(x2, x4)) && (max(y1, y3) <= min(y2, y4)))
                return true;
            else
                return false;
        }
        template <typename T> bool checkSquareCrash(
            T X1, T Y1, T R1,
            T X2, T Y2, T R2
        )
        {
            T x1 = X1 - R1, x2 = X1 + R1; T y1 = Y1 + R1, y2 = Y1 - R1;
            T x3 = X2 - R2, x4 = X2 + R2; T y3 = Y2 + R2, y4 = Y2 - R2;
            return seg::Math::checkRectCrash<T>(
                x1, y1, x2, y2, x3, y3, x4, y4
            );
        }
    } // namespace Math

    // long string, read cfg, ...

} // namespace seg

#define pass seg::ELLIPSIS();

//#include "seg.cpp"
